#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <unistd.h>
#include <time.h>

#define NUM_PHILOSOPHERS 5
#define THINK_TIME 1
#define EAT_TIME 2

union semun
{
    int val;//设置单个信号量的值
    struct semid_ds *buf;//获取/设置信号量集的元数据（权限、时间戳等）
    unsigned short *array;//批量获取/设置信号量集中所有信号量的值
    struct seminfo *__buf;//获取系统级信号量信息（极少使用）
};

void P(int sem_id, int sem_num)
{
    struct sembuf op = {sem_num, -1, SEM_UNDO};
    semop(sem_id, &op, 1);
}

// V操作（释放信号量）
void V(int sem_id, int sem_num) 
{
    struct sembuf op = {sem_num, 1, SEM_UNDO};
    semop(sem_id, &op, 1);
}

void philosopher(int id,int sem_id)
{
    srand(time(NULL) ^ (getpid() << 16));
    int left = id;
    int right = (id + 1) % NUM_PHILOSOPHERS;
    while(1)
    {
        //思考
        printf("Philosopher %d is thinking...\n", id);
        sleep(THINK_TIME + rand() % 2);

        // 尝试拿筷子   
        printf("Philosopher %d is hungry, trying to pick up chopsticks %d and %d\n", id, left, right);
        if(id == NUM_PHILOSOPHERS - 1)
        {
            //先拿右筷子，再那左筷子
            P(sem_id, right);
            printf("Philosopher %d picked up right chopstick %d\n", id, right);
            P(sem_id, left);
            printf("Philosopher %d picked up left chopstick %d\n", id, left);
        }
        else // 其他哲学家先拿左筷子
        { 
            P(sem_id, left);
            printf("Philosopher %d picked up left chopstick %d\n", id, left);
            P(sem_id, right);
            printf("Philosopher %d picked up right chopstick %d\n", id, right);
        }
        // 进餐
        printf("Philosopher %d is eating...\n", id);
        sleep(EAT_TIME + rand() % 2);

        // 放回筷子
        V(sem_id, left);
        V(sem_id, right);
        printf("Philosopher %d put down chopsticks %d and %d\n", id, left, right);
    }
}

int main()
{
    key_t key = ftok(".",'p');
    int sem_id = semget(key,NUM_PHILOSOPHERS,IPC_CREAT | 0666);//创建信号量集
    if(sem_id == -1)
    {
        perror("semget failed");
        exit(1);
    }
    //初始化信号量（每只筷子初始值都为1）
    union semun arg;
    unsigned short values[NUM_PHILOSOPHERS] = {1,1,1,1,1};
    arg.array = values;
    if(semctl(sem_id, 0, SETALL, arg) == -1)
    {
        perror("semctl SETALL failed");
         exit(1);
    }
    //创建5个子进程
    for(int i = 0;i < NUM_PHILOSOPHERS;i++)
    {
        pid_t pid = fork();
        if(pid == 0)//子进程
        {
            philosopher(i, sem_id); // 子进程执行哲学家行为
            exit(0);
        }
        else if(pid < 0) //创建失败
        {
            perror("fork failed");
            exit(1);
        }
    }

    while(wait(NULL) > 0);
    semctl(sem_id, 0, IPC_RMID, 0); // 删除信号量集
    return 0;
}
